package pCloud.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;

import pCloud.config.PCloudConfig;
import pCloud.config.PCloudRequestConfig;
import pCloud.exception.InvalidClientRequest;
import pCloud.message.ClientRequest;
import pCloud.message.DatabaseRequest;
import pCloud.message.Message;
import pCloud.message.NumberMessage;
import pCloud.message.PolicyFile;
import pCloud.message.PolicyManagementRequest;
import pCloud.message.RequestFile;
import pCloud.message.RequestedResource;
import pCloud.server.ProxyServer;
import pCloud.transport.Communicator;
import pCloud.transport.SocketCommunicator;
import pCloud.transport.SocketTransportHandle;

public class Client {

	Connector connector;
	InetSocketAddress proxyServerAddress;

	Communicator communicator;
	int clientId;
	Logger logger;

	String configFile = PCloudConfig.DEFAULT_CONFIG_FILE;
	InetSocketAddress localAddress;

	public Client(int clientId, String configFile) throws Exception {
		this.clientId = clientId;
		this.communicator = new SocketCommunicator();
		this.configFile = configFile;
		init();
		startConnection();
	}

	void init() throws Exception {
		PCloudConfig.init(this.configFile);

		logger = initLog("Client" + clientId);
		communicator.init(logger);

		String[] addr = PCloudConfig.getString(
				PCloudConfig.PCLOUD_PROXY_SERVER_ADDRESS).split(" ");
		String hostName = addr[0];
		int port = new Integer(addr[1]).intValue();

		connector = new ClientProxyServerConnector(hostName, port,
				this.communicator);

		proxyServerAddress = new InetSocketAddress(hostName, port);
	}

	Logger initLog(String name) {
		Logger logger = Logger.getLogger(name);
		SimpleLayout layout = new SimpleLayout();

		FileAppender appender = null;
		try {
			appender = new FileAppender(layout,
					PCloudConfig.getString(PCloudConfig.PCLOUD_CLIENT_LOG),
					false);
		} catch (Exception e) {
			System.err.println("Error initializing log file for " + name);
			e.printStackTrace();
		}

		logger.addAppender(appender);
		return logger;
	}

	private void startConnection() throws Exception {
		connector.connect();
		this.communicator.addConnection(connector.protocolHandler);
		this.localAddress = (InetSocketAddress) connector.protocolHandler
				.getTransportHanlde().getLocalAddress();
	}

	/**
	 * Loading the policyFile, to specified server name
	 * 
	 * @param policyFileName
	 * @throws Exception
	 */
	public void loadPolicy(String policyFileName, String serverIp,
			int serverPort) throws Exception {

		communicator.getLogger().debug(
				"Uploading policy " + policyFileName + " onto server "
						+ serverIp + ":" + serverPort);

		InetSocketAddress isa = new InetSocketAddress(serverIp, serverPort);
		PolicyManagementRequest pmr = null;

		PolicyFile policyFile = new PolicyFile();
		policyFile.initializeContentFromFile(policyFileName);
		pmr = new PolicyManagementRequest(PolicyManagementRequest.ADD,
				policyFile);

		Message msg = new Message(this.localAddress, isa, pmr,
				Message.CLIENT_TO_PROXY_MSG);
		communicator.send(proxyServerAddress, msg);
	}

	public void removePolicy(int policyIndex, String serverIp, int serverPort)
			throws Exception {
		communicator.getLogger().debug(
				"Removing policy from " + serverIp + ":" + serverPort
						+ " with index " + policyIndex);

		InetSocketAddress isa = new InetSocketAddress(serverIp, serverPort);
		PolicyManagementRequest pmr = null;

		pmr = new PolicyManagementRequest(PolicyManagementRequest.REMOVE,
				policyIndex);

		Message msg = new Message(this.localAddress, isa, pmr,
				Message.CLIENT_TO_PROXY_MSG);
		communicator.send(proxyServerAddress, msg);
	}

	/**
	 * Fetching active policies in remote servers
	 * 
	 * @param serverIp
	 * @param serverPort
	 * @throws Exception
	 */
	public void queryPolicy(String serverIp, int serverPort) throws Exception {
		this.communicator.getLogger().debug(
				"Query active policies on remote server " + serverIp + ":"
						+ serverPort);
		InetSocketAddress isa = new InetSocketAddress(serverIp, serverPort);

		PolicyManagementRequest pmr = new PolicyManagementRequest(
				PolicyManagementRequest.QUERY, 0);
		Message msg = new Message(this.localAddress, isa, pmr,
				Message.CLIENT_TO_PROXY_MSG);
		communicator.send(proxyServerAddress, msg);
	}

	public void requestDataFromServer(String requestConfigFile)
			throws Exception {
		PCloudRequestConfig.init(requestConfigFile);

		ArrayList<RequestedResource> resources = PCloudRequestConfig
				.parseConfig();
		this.communicator.getLogger().debug(
				"Constructing client request for " + resources.size()
						+ " resources");

		ClientRequest clientRequest = new ClientRequest();

		RequestedResource rr;
		for (int i = 0; i < resources.size(); i++) {
			rr = resources.get(i);
			clientRequest.addRequestedResource(rr, rr.getJoiningColumn());
		}
		
		// requestFile.initializeContentFromFile("requestFile.xml");
		Message msg = new Message(this.localAddress, null, clientRequest,
				Message.CLIENT_TO_PROXY_MSG);
		communicator.send(proxyServerAddress, msg);
	}

	public void sendMessages() throws Exception {
		// 2. Repeatedly sends messages to the n data servers
		// 2.1. Read the lists of data server.
		String[] dS = PCloudConfig.getString(
				PCloudConfig.PCLOUD_DATA_SERVER_ADDRESS).split(" ");
		int nDataServers = new Integer(dS[0]).intValue();

		int j = 1;
		InetSocketAddress[] addresses = new InetSocketAddress[nDataServers];
		for (int i = 0; i < nDataServers; i++) {
			addresses[i] = new InetSocketAddress(dS[j],
					new Integer(dS[j + 1]).intValue());
			j += 2;
		}

		// 2.2. Send messages
		Message msg;

		InetSocketAddress from = (InetSocketAddress) connector.protocolHandler
				.getTransportHanlde().getLocalAddress();

		DatabaseRequest request = new DatabaseRequest(
				"select tempout from temp where tempout > 32");

		PolicyManagementRequest pmr = null;

		PolicyFile policyFile = new PolicyFile();
		policyFile.initializeContentFromFile("policyFile.xml");
		pmr = new PolicyManagementRequest(PolicyManagementRequest.ADD,
				policyFile);

		msg = new Message(from, addresses[1], pmr, Message.CLIENT_TO_PROXY_MSG);
		communicator.send(proxyServerAddress, msg);

		policyFile = new PolicyFile();
		policyFile.initializeContentFromFile("anotherPolicyFile.xml");
		pmr = new PolicyManagementRequest(PolicyManagementRequest.ADD,
				policyFile);
		msg = new Message(from, addresses[0], pmr, Message.CLIENT_TO_PROXY_MSG);
		communicator.send(proxyServerAddress, msg);

		// pmr = new PolicyManagementRequest(PolicyManagementRequest.REMOVE, 2);
		// msg = new Message(from, addresses[1], pmr,
		// Message.CLIENT_TO_PROXY_MSG);
		// communicator.send(proxyServerAddress, msg);

		// ClientRequest clientRequest = new ClientRequest("anhdinh",
		// "government");
		// RequestedResource rr = new RequestedResource(addresses[1],
		// "jdbc:postgresql:postgres", "temp", null, "read");
		// rr.addColumnId("samplingtime");
		// rr.addColumnId("tempout");
		// RequestedResource rr1 = new RequestedResource(addresses[0],
		// "jdbc:postgresql:postgres", "temp", null, "read");
		// rr1.addColumnId("samplingtime");
		// rr1.addColumnId("humout");

		// clientRequest.addRequestedResource(rr, null);
		// clientRequest.addRequestedResource(rr1, null);
		//
		// // requestFile.initializeContentFromFile("requestFile.xml");
		// msg = new Message(from, addresses[1], clientRequest,
		// Message.CLIENT_TO_PROXY_MSG);
		// communicator.send(proxyServerAddress, msg);
		//
		// pmr = new PolicyManagementRequest(PolicyManagementRequest.QUERY, 0);
		// msg = new Message(from, addresses[1], pmr,
		// Message.CLIENT_TO_PROXY_MSG);
		// communicator.send(proxyServerAddress, msg);
	}

	public static void main(String[] args) throws Exception {

		if (args.length < 1)
			throw new InvalidClientRequest("Config file needed");

		System.out.println("Start_time "+System.currentTimeMillis());

		if (args[1].equals("-help")) {
			System.out
					.println("Client <configFile> -load <policyFile> <host:port>: adding policy");
			System.out
					.println("Client <configFile> -remove <policyIndex> <host:port>: remove policy");
			System.out
					.println("Client <configFile> -query <host:port>: query active policies in remote data servers");
			System.out
					.println("Client <configFile> -request <requestConfigFile>: request data, parameters read from the config file");
			return;
		} else if (args[1].equals("-load")) {
			if (args.length != 4)
				throw new InvalidClientRequest(
						"Loading operation needs 4 parameters");

			Client client = new Client(0, args[0]);

			String policyFile = args[2];
			String[] address = args[3].split(":");
			client.loadPolicy(policyFile, address[0],
					new Integer(address[1]).intValue());
			return;

		} else if (args[1].equals("-remove")) {
			if (args.length != 4)
				throw new InvalidClientRequest(
						"Removing policy needs 4 parameters");
			Client client = new Client(0, args[0]);

			int policyIndex = new Integer(args[2]).intValue();
			String[] address = args[3].split(":");
			client.removePolicy(policyIndex, address[0],
					new Integer(address[1]).intValue());
			return;

		} else if (args[1].equals("-query")) {
			if (args.length < 3)
				throw new InvalidClientRequest(
						"Query operation needs 3 parameters");
			String[] address = args[2].split(":");
			Client client = new Client(0, args[0]);

			client.queryPolicy(address[0], new Integer(address[1]).intValue());
			return;

		} else if (args[1].equals("-request")) {
			if (args.length < 3)
				throw new InvalidClientRequest(
						"Request operation needs 3 parameters");
			Client client = new Client(0, args[0]);
			String requestFile = args[2];
			client.requestDataFromServer(requestFile);
			return;
		} else
			throw new InvalidClientRequest(
					"Client request is no well-formed. Use -help for details");
	}
}
